/*
 * Copyright (c) 2012 Jonathan Tyers
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package uk.org.uplink.configproxy;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.List;

import javax.inject.Inject;

import org.apache.commons.configuration.HierarchicalConfiguration;
import org.apache.commons.configuration.event.ConfigurationListener;

/**
 * Wraps a {@link HierarchicalConfiguration}, providing:
 * <ul><li>a 'containsKey' method that will return true for keys that exist in the underlying
 * configuration even if no value is specified for those keys</li>
 * <li>a generic 'get' method that allows the return type to be specified</li>
 * <li>the ability to add listeners to the underlying delegate</li>
 * </ul>
 * 
 * @author jonny
 */
public class HierarchicalConfigurationWrapper {
	private final HierarchicalConfiguration delegate;

	@Inject
	public HierarchicalConfigurationWrapper(final HierarchicalConfiguration delegate) {
		super();
		this.delegate = delegate;
	}

	public boolean containsKeyRegardlessOfValue(final String key) {
		/* first check if an attribute exists there */
		boolean result = delegate.containsKey(key);
		if (result) {
			return result;
		}

		/* if that failed, try to see if a node exists there by creating a subconfiguration at that key */
		try {
			delegate.configurationAt(key);
			result = true;
		} catch (IllegalArgumentException e) {
			result = false;
		}

		return result;
	}

	@SuppressWarnings({ "unchecked", "rawtypes" })
	public Object get(final Class<?> cls, final String key, final Object defaultValue) {
		if (Boolean.class.equals(cls) || Boolean.TYPE.equals(cls)) {
			return delegate.getBoolean(key, (Boolean) defaultValue);

		} else if (String.class.equals(cls)) {
			return delegate.getString(key, (String) defaultValue);

		} else if (Number.class.isAssignableFrom(cls) || cls.isPrimitive()) {
			if (Integer.class.equals(cls) || Integer.TYPE.equals(cls)) {
				return delegate.getInteger(key, (Integer) defaultValue);

			} else if (Long.class.equals(cls) || Long.TYPE.equals(cls)) {

				return delegate.getLong(key, (Long) defaultValue);
			} else if (Byte.class.equals(cls) || Byte.TYPE.equals(cls)) {
				return delegate.getByte(key, (Byte) defaultValue);

			} else if (Short.class.equals(cls) || Short.TYPE.equals(cls)) {

				return delegate.getShort(key, (Short) defaultValue);
			} else if (Float.class.equals(cls) || Float.TYPE.equals(cls)) {
				return delegate.getFloat(key, (Float) defaultValue);

			} else if (Double.class.equals(cls) || Double.TYPE.equals(cls)) {

				return delegate.getDouble(key, (Double) defaultValue);
			} else if (BigInteger.class.equals(cls)) {
				return delegate.getBigInteger(key, (BigInteger) defaultValue);

			} else if (BigDecimal.class.equals(cls)) {
				return delegate.getBigDecimal(key, (BigDecimal) defaultValue);
			}

		} else if (List.class.equals(cls)) {
			return delegate.getList(key, (List) defaultValue);
		}

		throw new IllegalArgumentException("The type '" + cls.getCanonicalName() + " is not supported");
	}

	public void addConfigurationListener(final ConfigurationListener l) {
		delegate.addConfigurationListener(l);
	}
}
